package cn.edu.njnu.opengmpback.common.utils;

import cn.edu.njnu.opengmpback.common.dao.GeoServerItemDao;
import cn.edu.njnu.opengmpback.common.entity.GeoServerItem;
import com.alibaba.fastjson.JSONObject;
import it.geosolutions.geoserver.rest.GeoServerRESTManager;
import it.geosolutions.geoserver.rest.GeoServerRESTPublisher;
import it.geosolutions.geoserver.rest.GeoServerRESTReader;
import it.geosolutions.geoserver.rest.decoder.*;
import it.geosolutions.geoserver.rest.encoder.GSAbstractStoreEncoder;
import it.geosolutions.geoserver.rest.encoder.GSLayerEncoder;
import it.geosolutions.geoserver.rest.encoder.GSResourceEncoder;
import it.geosolutions.geoserver.rest.encoder.coverage.GSCoverageEncoder;
import it.geosolutions.geoserver.rest.encoder.datastore.*;
import it.geosolutions.geoserver.rest.encoder.feature.GSFeatureTypeEncoder;
import it.geosolutions.geoserver.rest.manager.GeoServerRESTStoreManager;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import java.io.File;
import java.io.FileNotFoundException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.Base64;
import java.util.Date;
import java.util.List;
import java.util.Optional;

/**
 * Created by HYQ on 2021/8/16 11:17
 **/

@Component
public class GeoServerUtils {

    @Value("${geoserver.url}")
    private String url;

    @Value("${geoserver.username}")
    private String username;

    @Value("${geoserver.password}")
    private String passWord;

    @Autowired
    GeoServerItemDao geoServerItemDao;

    RestTemplate restTemplate = new RestTemplate();


    /**
     * @param workSpace: 工作空间名称
     * @param storeName: 存储库名称
     * @param tiffPath:  tiff文件路径
     * @return void
     * @description 将tiff文件发布为GeoServer服务
     * @author HYQ
     * @date 2021/8/16 19:44
     */
    public void PublishTiff(String workSpace, String storeName, String layerName, String styleName, String tiffPath) throws MalformedURLException {
        /**Geoserver 连接配置**/
//        判断workspace是否存在，不存在创建
        GeoServerRESTManager manager = new GeoServerRESTManager(new URL(url), username, passWord);
        GeoServerRESTPublisher publisher = manager.getPublisher();
        GeoServerRESTReader restReader = manager.getReader();
        List<String> workSpaces = manager.getReader().getNamespaceNames();
        if (!workSpaces.contains(workSpace)) {
            boolean createws = publisher.createWorkspace(workSpace);
            System.out.println("create workspace: " + createws);
        } else {
            System.out.println("workspace已经存在了,workspace:" + workSpace);
        }
//        判断数据存储是否存在，不存在创建
        RESTDataStore restDataStore = manager.getReader().getDatastore(workSpace, storeName);
        if (restDataStore == null) {
            GSGeoTIFFDatastoreEncoder gsGeoTIFFDatastoreEncoder = new GSGeoTIFFDatastoreEncoder(storeName);
            gsGeoTIFFDatastoreEncoder.setWorkspaceName(workSpace);
            gsGeoTIFFDatastoreEncoder.setUrl(new URL("file://" + tiffPath));

//            创建数据存储
            boolean creatStore = manager.getStoreManager().create(workSpace, gsGeoTIFFDatastoreEncoder);
            System.out.println("create TiFFStore : " + creatStore);
//            发布图层

            if (!restReader.existsLayer(workSpace, layerName)) {
                GSCoverageEncoder gsCoverageEncoder = new GSCoverageEncoder();
                gsCoverageEncoder.setTitle(layerName);
                gsCoverageEncoder.setName(layerName);
                gsCoverageEncoder.setSRS("EPSG:3857");
                gsCoverageEncoder.setProjectionPolicy(GSResourceEncoder.ProjectionPolicy.REPROJECT_TO_DECLARED);
                gsCoverageEncoder.setNativeName(layerName);


                GSLayerEncoder gsLayerEncoder = new GSLayerEncoder();
                if(styleName != null){
                    gsLayerEncoder.setDefaultStyle(workSpace, styleName);
                }
                if(publisher.publishMosaicLayer(workSpace, storeName, gsCoverageEncoder, gsLayerEncoder)){
                    GeoServerItem geoServerItem = new GeoServerItem();
                    geoServerItem.setCreateTime(new Date());
                    geoServerItem.setUpdateTime(new Date());
                    geoServerItem.setWorkspace(workSpace);
                    geoServerItem.setStoreName(storeName);
                    geoServerItem.setStoreType("Image");
                    geoServerItemDao.save(geoServerItem);
                    removeStoreOverNum(manager);
                }else {
                    System.out.println("图层:" + layerName + " 发布失败!");
                }
            } else {
                System.out.println("图层:" + layerName + " 已经存在了!");
            }
        } else {
            Optional<GeoServerItem> optionalGeoServerItem = geoServerItemDao.findGeoServerItemByWorkspaceAndStoreName(workSpace, storeName);
            if(optionalGeoServerItem.isPresent()){
                GeoServerItem geoServerItem = optionalGeoServerItem.get();
                geoServerItem.setUpdateTime(new Date());
                geoServerItemDao.save(geoServerItem);
            }
            System.out.println("数据存储已经存在了,store:" + storeName);
        }
    }

    /**
     * @param workSpace: 工作区
     * @param storeName: 数据存储名称
     * @param layerName: 图层名
     * @param styleName: 样式名
     * @param shapefileZipPath: Shapefile ZIP文件路径
     * @return boolean
     * @description 通过ZIP文件发布Shapefile到GeoServer (符合官方最佳实践)
     * @author HYQ + Claude Assistant
     * @date 2025-08-20
     */
    public boolean PublishShapefileZip(String workSpace, String storeName, String layerName, String styleName, String shapefileZipPath) {
        try {
            // GeoServer连接配置
            GeoServerRESTManager manager = new GeoServerRESTManager(new URL(url), username, passWord);
            GeoServerRESTPublisher publisher = manager.getPublisher();
            GeoServerRESTReader restReader = manager.getReader();
            
            // 确保工作空间存在
            List<String> workSpaces = restReader.getNamespaceNames();
            if (!workSpaces.contains(workSpace)) {
                boolean createws = publisher.createWorkspace(workSpace);
                System.out.println("✅ 创建工作空间: " + workSpace + " -> " + createws);
            }
            
            // 检查数据存储是否已存在
            RESTDataStore restDataStore = manager.getReader().getDatastore(workSpace, storeName);
            if (restDataStore != null) {
                System.out.println("⚠️ 数据存储已存在: " + storeName);
                return true; // 如果存储已存在，认为成功
            }
            
            // 构建ZIP上传的REST API URL - 这是GeoServer官方推荐方式
            String uploadUrl = url + "/rest/workspaces/" + workSpace + "/datastores/" + storeName + "/file.shp";
            
            // 读取ZIP文件内容
            File zipFile = new File(shapefileZipPath);
            if (!zipFile.exists()) {
                System.out.println("❌ ZIP文件不存在: " + shapefileZipPath);
                return false;
            }
            
            // 构建HTTP请求头和认证
            String auth = username + ":" + passWord;
            String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes());
            
            // 使用RestTemplate上传ZIP文件
            org.springframework.http.HttpHeaders headers = new org.springframework.http.HttpHeaders();
            headers.set("Authorization", "Basic " + encodedAuth);
            headers.set("Content-Type", "application/zip");
            
            // 读取文件为字节数组
            byte[] zipContent = java.nio.file.Files.readAllBytes(zipFile.toPath());
            
            org.springframework.http.HttpEntity<byte[]> entity = new org.springframework.http.HttpEntity<>(zipContent, headers);
            
            try {
                // 执行PUT请求上传ZIP文件
                org.springframework.http.ResponseEntity<String> response = restTemplate.exchange(
                    uploadUrl, 
                    org.springframework.http.HttpMethod.PUT, 
                    entity, 
                    String.class
                );
                
                if (response.getStatusCode().is2xxSuccessful()) {
                    System.out.println("✅ Shapefile ZIP成功上传到GeoServer: " + layerName);
                    
                    // 保存到本地数据库记录
                    GeoServerItem geoServerItem = new GeoServerItem();
                    geoServerItem.setCreateTime(new Date());
                    geoServerItem.setUpdateTime(new Date());
                    geoServerItem.setWorkspace(workSpace);
                    geoServerItem.setStoreName(storeName);
                    geoServerItem.setStoreType("Shape");
                    geoServerItemDao.save(geoServerItem);
                    
                    // 如果需要设置样式
                    if (styleName != null) {
                        try {
                            // 这里可以添加样式设置逻辑
                            System.out.println("🎨 应用样式: " + styleName);
                        } catch (Exception styleEx) {
                            System.out.println("⚠️ 样式设置失败，但图层发布成功: " + styleEx.getMessage());
                        }
                    }
                    
                    removeStoreOverNum(manager);
                    return true;
                } else {
                    System.out.println("❌ Shapefile ZIP上传失败，HTTP状态码: " + response.getStatusCode());
                    System.out.println("响应内容: " + response.getBody());
                    return false;
                }
                
            } catch (Exception httpEx) {
                System.out.println("❌ HTTP请求失败: " + httpEx.getMessage());
                httpEx.printStackTrace();
                return false;
            }
            
        } catch (Exception e) {
            System.out.println("❌ PublishShapefileZip执行失败: " + e.getMessage());
            e.printStackTrace();
            return false;
        }
    }

    /**
     * @param shapefileDirectory: Shapefile组件文件所在目录
     * @param baseName: Shapefile基名（不含扩展名）
     * @return String ZIP文件路径
     * @description 将Shapefile组件文件打包成ZIP文件 
     * @author Claude Assistant
     * @date 2025-08-20
     */
    public String createShapefileZip(String shapefileDirectory, String baseName) {
        try {
            // 定义ZIP文件路径
            String zipPath = shapefileDirectory + "/" + baseName + ".zip";
            
            // 定义需要打包的Shapefile组件文件扩展名
            String[] extensions = {".shp", ".shx", ".dbf", ".prj", ".sbn", ".sbx", ".cpg"};
            
            try (java.util.zip.ZipOutputStream zipOut = new java.util.zip.ZipOutputStream(
                new java.io.FileOutputStream(zipPath))) {
                
                int filesAdded = 0;
                for (String ext : extensions) {
                    File componentFile = new File(shapefileDirectory, baseName + ext);
                    if (componentFile.exists()) {
                        // 添加文件到ZIP
                        java.util.zip.ZipEntry zipEntry = new java.util.zip.ZipEntry(baseName + ext);
                        zipOut.putNextEntry(zipEntry);
                        
                        try (java.io.FileInputStream fileIn = new java.io.FileInputStream(componentFile)) {
                            byte[] buffer = new byte[1024];
                            int length;
                            while ((length = fileIn.read(buffer)) >= 0) {
                                zipOut.write(buffer, 0, length);
                            }
                        }
                        zipOut.closeEntry();
                        filesAdded++;
                        System.out.println("📦 已添加到ZIP: " + baseName + ext);
                    }
                }
                
                if (filesAdded >= 3) { // 至少需要.shp, .shx, .dbf
                    System.out.println("✅ Shapefile ZIP创建成功: " + zipPath + " (包含 " + filesAdded + " 个文件)");
                    return zipPath;
                } else {
                    System.out.println("❌ Shapefile组件文件不足，无法创建有效ZIP (仅找到 " + filesAdded + " 个文件)");
                    // 删除无效的ZIP文件
                    new File(zipPath).delete();
                    return null;
                }
            }
        } catch (Exception e) {
            System.out.println("❌ 创建Shapefile ZIP失败: " + e.getMessage());
            e.printStackTrace();
            return null;
        }
    }
    public boolean PublishShape(String workSpace, String storeName, String layerName, String styleName, String shapePath) {
//        判断workspace是否存在，不存在创建
        GeoServerRESTManager manager = null;
        try {
            manager = new GeoServerRESTManager(new URL(url), username, passWord);
        } catch (MalformedURLException e) {
            e.printStackTrace();
            System.out.println("Geoserver连接失败！");
            return false;
        }
        GeoServerRESTPublisher publisher = manager.getPublisher();
        GeoServerRESTReader restReader = manager.getReader();
        List<String> workSpaces = restReader.getNamespaceNames();
        if (!workSpaces.contains(workSpace)) {
            boolean createws = publisher.createWorkspace(workSpace);
            System.out.println("workspace:" + createws + " 创建成功!");
        } else {
            System.out.println("workspace:" + workSpace + " 已经存在了!");
        }
//        判断数据存储是否存在，不存在创建
        RESTDataStore restDataStore = manager.getReader().getDatastore(workSpace, storeName);

        if (restDataStore == null) {
            GSShapefileDatastoreEncoder gsShapefileDatastoreEncoder = null;
            try {
                gsShapefileDatastoreEncoder = new GSShapefileDatastoreEncoder(storeName, new URL("file:" + shapePath));
            } catch (MalformedURLException e) {
                e.printStackTrace();
                System.out.println("数据存储创建失败！");
                return false;
            }
            gsShapefileDatastoreEncoder.setCharset(Charset.forName("GBK"));

//            创建数据存储
            manager.getStoreManager().create(workSpace, gsShapefileDatastoreEncoder);

//            发布图层
            if (!restReader.existsLayer(workSpace, layerName)) {
                GSFeatureTypeEncoder gsFeatureTypeEncoder = new GSFeatureTypeEncoder();
                gsFeatureTypeEncoder.setTitle(layerName);
                gsFeatureTypeEncoder.setName(layerName);

//                gsFeatureTypeEncoder.setSRS(GeoServerRESTPublisher.DEFAULT_CRS);

                //由于需要在mapbox上进行可视化，所以要将坐标系强制转为3857否则将会出现不能显示或者出错的情况
                gsFeatureTypeEncoder.setSRS("EPSG:3857"); // set the default SRS to EPSG:4326
                gsFeatureTypeEncoder.setProjectionPolicy(GSResourceEncoder.ProjectionPolicy.REPROJECT_TO_DECLARED);

                GSLayerEncoder gsLayerEncoder = new GSLayerEncoder();
                if(styleName != null){
                    gsLayerEncoder.setDefaultStyle(workSpace, styleName);
                }

                boolean layer = publisher.publishDBLayer(workSpace, storeName, gsFeatureTypeEncoder, gsLayerEncoder);
                if (layer) {
                    System.out.println("图层: " + layerName + " 发布成功！");
                    GeoServerItem geoServerItem = new GeoServerItem();
                    geoServerItem.setCreateTime(new Date());
                    geoServerItem.setUpdateTime(new Date());
                    geoServerItem.setWorkspace(workSpace);
                    geoServerItem.setStoreName(storeName);
                    geoServerItem.setStoreType("Shape");
                    geoServerItemDao.save(geoServerItem);
                    removeStoreOverNum(manager);
                }else {
                    System.out.println("图层: " + layerName + " 发布失败！");
                }

            } else {
                System.out.println("图层:" + layerName + " 已经存在了!");
            }
        } else {
            Optional<GeoServerItem> optionalGeoServerItem = geoServerItemDao.findGeoServerItemByWorkspaceAndStoreName(workSpace, storeName);
            if(optionalGeoServerItem.isPresent()){
                GeoServerItem geoServerItem = optionalGeoServerItem.get();
                geoServerItem.setUpdateTime(new Date());
                geoServerItemDao.save(geoServerItem);
            }
            System.out.println("数据存储:" + storeName + " 已经存在了!");
        }
        return true;
    }

    /**
     * @param workSpace: 工作区
     * @param storeType: 数据类型：[“Image”,“Shape”]
     * @param storeName: 数据存储名称
     * @return boolean
     * @description 删除指定工作空间下的数据存储
     * @author HYQ
     * @date 2021/12/2 16:13
     */
    public boolean removeStore(String workSpace, String storeType, String storeName) {
        GeoServerRESTManager manager = null;
        try {
            manager = new GeoServerRESTManager(new URL(url), username, passWord);
        } catch (MalformedURLException e) {
            e.printStackTrace();
            System.out.println("Geoserver连接失败！");
            return false;
        }

        String id = null;
        Optional<GeoServerItem> optionalGeoServerItem = geoServerItemDao.findGeoServerItemByWorkspaceAndStoreName(workSpace, storeName);
        if(optionalGeoServerItem.isPresent()){
             id = optionalGeoServerItem.get().getId();
        }


        GeoServerRESTReader restReader = manager.getReader();
        List<String> workSpaces = restReader.getNamespaceNames();
        if (!workSpaces.contains(workSpace)) {
            System.out.println("workspace:" + workSpace + " 不存在!");
            if(id != null){
                geoServerItemDao.deleteById(id);
            }
            return false;
        }

        //删除数据存储
        GeoServerRESTStoreManager storeManager = manager.getStoreManager();
        GSAbstractStoreEncoder gsAbstractStoreEncoder;
        try {
            boolean removeResult;
            switch (storeType) {
                case "Shape":
                    gsAbstractStoreEncoder = new GSAbstractStoreEncoder(GeoServerRESTPublisher.StoreType.DATASTORES, storeName) {
                        @Override
                        protected String getValidType() {
                            return null;
                        }
                    };
                    gsAbstractStoreEncoder.setName(storeName);
                    removeResult = storeManager.remove(workSpace, gsAbstractStoreEncoder, true);
                    break;
                case "Image":
                    gsAbstractStoreEncoder = new GSAbstractStoreEncoder(GeoServerRESTPublisher.StoreType.COVERAGESTORES, storeName) {
                        @Override
                        protected String getValidType() {
                            return null;
                        }
                    };
                    gsAbstractStoreEncoder.setName(storeName);
                    removeResult = storeManager.remove(workSpace, gsAbstractStoreEncoder, true);
                    break;
                default:
                    throw new IllegalStateException("Unexpected value: " + storeType);
            }
            if (removeResult){
                if(id != null){
                    geoServerItemDao.deleteById(id);
                }
                System.out.println("数据存储：" + workSpace + ":" + storeName + "删除成功！");
            }else {
                System.out.println("数据存储：" + workSpace + ":" + storeName + "删除失败！");
            }
            return removeResult;

        } catch (MalformedURLException e) {
            System.out.println("数据存储：" + workSpace + ":" + storeName + "删除失败！");
            e.printStackTrace();
        }

        return false;
    }


    /**
     * @param workSpace: 工作区
     * @return boolean
     * @description 删除指定工作空间
     * @author lhy
     */
    public boolean removeWorkspace(String workSpace) {
        GeoServerRESTManager manager = null;
        try {
            manager = new GeoServerRESTManager(new URL(url), username, passWord);
        } catch (MalformedURLException e) {
            e.printStackTrace();
            System.out.println("Geoserver连接失败！");
            return false;
        }

        GeoServerRESTReader restReader = manager.getReader();
        List<String> workSpaces = restReader.getNamespaceNames();
        if (!workSpaces.contains(workSpace)) {
            System.out.println("workspace:" + workSpace + " 不存在!");
            return false;
        }
        GeoServerRESTPublisher publisher = manager.getPublisher();
        boolean removeResult = publisher.removeWorkspace(workSpace, true);
        if (removeResult){
            System.out.println("工作空间：" + workSpace + "删除成功!");
        }else {
            System.out.println("工作空间：" + workSpace + "删除失败!");
        }
        return removeResult;
    }

    /**
     * @param manager
     * @description 超过1000条数据存储删除最旧的
     * @author lhy
     */
    public void removeStoreOverNum(GeoServerRESTManager manager) {
        long a =  geoServerItemDao.count();
        if (geoServerItemDao.count() <= 1000) {
            return;
        }
        GeoServerItem geoServerItem = geoServerItemDao.findFirstByOrderByUpdateTimeAsc();
        String id = geoServerItem.getId();
        String workSpace = geoServerItem.getWorkspace();
        String storeName = geoServerItem.getStoreName();
        String storeType = geoServerItem.getStoreType();

        GeoServerRESTReader restReader = manager.getReader();
        List<String> workSpaces = restReader.getNamespaceNames();
        if (!workSpaces.contains(workSpace)) {
            System.out.println("workspace:" + workSpace + " 不存在!");
            geoServerItemDao.deleteById(id);
            return;
        }

//        删除数据存储
        GeoServerRESTStoreManager storeManager = manager.getStoreManager();
        GSAbstractStoreEncoder gsAbstractStoreEncoder;
        try {
            boolean removeResult;
            switch (storeType) {
                case "Shape":
                    gsAbstractStoreEncoder = new GSAbstractStoreEncoder(GeoServerRESTPublisher.StoreType.DATASTORES, storeName) {
                        @Override
                        protected String getValidType() {
                            return null;
                        }
                    };
                    gsAbstractStoreEncoder.setName(storeName);
                    removeResult = storeManager.remove(workSpace, gsAbstractStoreEncoder, true);
                    break;
                case "Image":
                    gsAbstractStoreEncoder = new GSAbstractStoreEncoder(GeoServerRESTPublisher.StoreType.COVERAGESTORES, storeName) {
                        @Override
                        protected String getValidType() {
                            return null;
                        }
                    };
                    gsAbstractStoreEncoder.setName(storeName);
                    removeResult = storeManager.remove(workSpace, gsAbstractStoreEncoder, true);
                    break;
                default:
                    throw new IllegalStateException("Unexpected value: " + storeType);
            }
            if (removeResult){
                System.out.println("数据存储：" + workSpace + ":" + storeName + "过期删除成功！");
                geoServerItemDao.deleteById(id);
            }else {
                System.out.println("数据存储：" + workSpace + ":" + storeName + "过期删除失败！");
            }

        } catch (MalformedURLException e) {
            System.out.println("数据存储：" + workSpace + ":" + storeName + "过期删除失败！");
            e.printStackTrace();
        }
    }
}
